export function lazyInst<T>(ctor?: new () => T): T {
  ctor ??= class {} as new () => T;
  let inst = null;

  return new Proxy(
    {},
    {
      apply(_, __: any, argArray: any[]) {
        inst ??= new ctor();
        return inst(...argArray);
      },

      construct(_, __: any[], ___: Function) {
        inst ??= new ctor();
        return inst;
      },

      defineProperty(
        _,
        property: string | symbol,
        attributes: PropertyDescriptor
      ) {
        inst ??= new ctor();
        Object.defineProperty(inst, property, attributes);
        return true;
      },

      deleteProperty(_, p: string | symbol) {
        inst ??= new ctor();
        delete inst[p];
        return true;
      },

      get(_, p: string | symbol, receiver: any) {
        inst ??= new ctor();
        return Reflect.get(inst, p, receiver);
      },

      getOwnPropertyDescriptor(_, p: string | symbol) {
        inst ??= new ctor();
        return Reflect.getOwnPropertyDescriptor(inst, p);
      },

      getPrototypeOf(_) {
        inst ??= new ctor();
        return Reflect.getPrototypeOf(inst);
      },

      has(_, p: string | symbol) {
        inst ??= new ctor();
        return Reflect.has(inst, p);
      },

      isExtensible(_) {
        inst ??= new ctor();
        return Reflect.isExtensible(inst);
      },

      ownKeys(_) {
        inst ??= new ctor();
        return Reflect.ownKeys(inst);
      },

      preventExtensions(_) {
        inst ??= new ctor();
        return Reflect.preventExtensions(inst);
      },

      set(_, p: string | symbol, newValue: any, receiver: any) {
        inst ??= new ctor();
        Reflect.set(inst, p, newValue, receiver);
        return true;
      },

      setPrototypeOf(_, v: object | null) {
        inst ??= new ctor();
        Reflect.setPrototypeOf(inst, v);
        return true;
      },
    }
  ) as T;
}

export const PATHS = [
  "./*.js",
  "./*.jsx",
  "./*.ts",
  "./*.tsx",
  "./*/index.js",
  "./*/index.jsx",
  "./*/index.ts",
  "./*/index.tsx",
];

const importPromises: Promise<any>[] = [];

export function assignImports(tgt: any, dict: Record<string, any>) {
  for (const key in dict) {
    const importPromise = dict[key]()
      .then((subModule: any) => {
        if (subModule?._assign_) {
          Object.assign(tgt, subModule._assign_);
        }
      })
      .catch((e: any) => {
        console.error(`Error importing ${key}`, e);
      });
    importPromises.push(importPromise);
  }
}

export async function waitForAllImports() {
  await Promise.allSettled(importPromises);
  importPromises.length = 0;
}
